//
//  ConduitsManager.m
//  Task Views
//
//  Created by Chris Karr on 2/23/09.
//  Copyright 2009 Chris J. Karr. All rights reserved.
//

#import <Security/Security.h>

#import "EMKeychainItem.h"
#import "EMKeychainProxy.h"

#import "ConduitsManager.h"
#import "BasecampConduit.h"
#import "iCalConduit.h"
#import "ThingsConduit.h"
#import "EntourageConduit.h"
#import "FogBugzConduit.h"
#import "OmniFocusConduit.h"

@implementation ConduitsManager

- (Conduit *) conduitForDefinition:(NSDictionary *) conduitDef
{
	if ([[conduitDef valueForKey:@"type"] isEqual:@"Basecamp"])
	{
		BasecampConduit * conduit = [[BasecampConduit alloc] init];
		
		return [conduit autorelease];
	}
	else if ([[conduitDef valueForKey:@"type"] isEqual:@"iCal"])
	{
		iCalConduit * conduit = [[iCalConduit alloc] init];
		
		return [conduit autorelease];
	}
	else if ([[conduitDef valueForKey:@"type"] isEqual:@"Things"])
	{
		ThingsConduit * conduit = [[ThingsConduit alloc] init];
		
		return [conduit autorelease];
	}
	else if ([[conduitDef valueForKey:@"type"] isEqual:@"Entourage"])
	{
		EntourageConduit * conduit = [[EntourageConduit alloc] init];
		
		return [conduit autorelease];
	}
	else if ([[conduitDef valueForKey:@"type"] isEqual:@"OmniFocus"])
	{
		OmniFocusConduit * conduit = [[OmniFocusConduit alloc] init];
		
		return [conduit autorelease];
	}
	else if ([[conduitDef valueForKey:@"type"] isEqual:@"FogBugz"])
	{
		FogBugzConduit * conduit = [[FogBugzConduit alloc] init];
		
		return [conduit autorelease];
	}
	else
	{
		NSLog (@"Unknown conduit: %@", [conduitDef valueForKey:@"type"]);
		return nil;
	}
}

- (Conduit *) currentConduit
{
	return [self conduitForDefinition:[[conduits selectedObjects] lastObject]];
}

- (IBAction) conduits:(id) sender
{
	[conduitsWindow makeKeyAndOrderFront:sender];
}

- (IBAction) add:(id) sender
{
	NSMutableDictionary * newConduit = [NSMutableDictionary dictionary];
	
	[newConduit setValue:@"New Conduit" forKey:@"name"];
	[newConduit setValue:@"Select Conduit Type" forKey:@"type"];
	[newConduit setValue:@"" forKey:@"site"];
	[newConduit setValue:@"" forKey:@"username"];

	[conduits addObject:newConduit];
	[conduits setSelectedObjects:[NSArray arrayWithObject:newConduit]];
}

- (NSString *) passwordForDefinition:(NSDictionary *) def
{
	if (def != nil && [def valueForKey:@"site"] != nil && [def valueForKey:@"username"] != nil)
	{
		EMInternetKeychainItem * keychainItem = [[EMKeychainProxy sharedProxy] internetKeychainItemForServer:[def valueForKey:@"site"]
																								withUsername:[def valueForKey:@"username"] 
																										path:@"" 
																										port:0 
																									protocol:kSecProtocolTypeHTTPS];
		
		if (keychainItem != nil)
			return [keychainItem password];
	}
	
	return nil;
}

- (void) updateRefreshes
{
	NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
	
	NSNumber * doRefresh = [defaults valueForKey:@"conduit_refresh"];
	
	if (doRefresh == nil)
		doRefresh = [NSNumber numberWithBool:NO];
	
	NSNumber * interval = [defaults valueForKey:@"refresh_interval"];
	
	if (interval == nil)
		interval = [NSNumber numberWithInteger:30];
	
	if ([doRefresh boolValue])
	{
		if (refreshTimer != nil)
		{
			[refreshTimer invalidate];
			[refreshTimer release];
		}
		
		refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:([interval floatValue] * 60) target:self selector:@selector(timerRefresh:) userInfo:nil repeats:NO] retain];
	}
}

- (void) timerRefresh:(NSTimer *) timer
{
	if (NSAlertDefaultReturn == NSRunAlertPanel(@"Refresh Conduits", @"Task Views is preparing to refresh the tasks in your database.", @"Proceed", @"Skip", nil))
		[self refreshAll:self];

	[self updateRefreshes];
}

- (IBAction) refreshAllTimer:(id) sender
{
	[self timerRefresh:refreshTimer];
}

- (void) awakeFromNib
{
	[GrowlApplicationBridge setGrowlDelegate:self];
	
	[conduits addObserver:self forKeyPath:@"selection" options:0 context:NULL];
	[[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"refresh_interval" options:0 context:NULL];
	[[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"conduit_refresh" options:0 context:NULL];

	[self willChangeValueForKey:@"password"];
	
	[self didChangeValueForKey:@"password"];
	
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(asRefresh:) name:CONDUIT_NOTE object:nil];
	
	refreshTimer = [[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(timerRefresh:) userInfo:nil repeats:NO] retain];
}

- (void) asRefresh:(NSNotification *) theNote
{
	[conduits setSelectedObjects:[NSArray arrayWithObject:[theNote object]]];
	
	[self refresh:self];
}

- (void) observeValueForKeyPath: (NSString *) keyPath ofObject:(id) object change:(NSDictionary *) change context:(void *) context
{
	if ([keyPath isEqual:@"refresh_interval"] || [keyPath isEqual:@"conduit_refresh"])
		[self updateRefreshes];
	else
	{
		[self willChangeValueForKey:@"password"];
		[self didChangeValueForKey:@"password"];
	}
}

- (NSString *) password
{
	NSDictionary * def = [[conduits selectedObjects] lastObject];

	return [self passwordForDefinition:def];
}

- (void) setPassword:(NSString *) newPassword
{
	if (newPassword == nil)
		newPassword = @"";
	
	NSDictionary * def = [[conduits selectedObjects] lastObject];

	NSLog (@"setting password '%@' for %@", newPassword, def);
	
	
	if (def != nil && [def valueForKey:@"site"] != nil && [def valueForKey:@"username"] != nil)
	{
		EMInternetKeychainItem * keychainItem = [[EMKeychainProxy sharedProxy] internetKeychainItemForServer:[def valueForKey:@"site"]
																								withUsername:[def valueForKey:@"username"] 
																										path:@"" 
																										port:0 
																									protocol:kSecProtocolTypeHTTPS];

		if (keychainItem == nil)
		{
			[[EMKeychainProxy sharedProxy] addInternetKeychainItemForServer:[def valueForKey:@"site"]
															   withUsername:[def valueForKey:@"username"]
																   password:newPassword
																	   path:@""
																	   port:0 
																   protocol:kSecProtocolTypeHTTPS];
		}
		else
			[keychainItem setPassword:newPassword];
	}
}

- (IBAction) remove:(id) sender
{
	NSDictionary * conduit = [[conduits selectedObjects] lastObject];
	
	NSString * title = [NSString stringWithFormat:@"Delete '%@'?", [conduit valueForKey:@"name"]];
	NSString * message = [NSString stringWithFormat:@"Are you sure that you wish to delete the conduit '%@'?", [conduit valueForKey:@"name"]];
	
	if (NSAlertDefaultReturn == NSRunAlertPanel(title, message, @"Yes", @"No", nil))
		[conduits removeObject:conduit];
}

- (IBAction) openExternalSite:(id) sender
{
	id conduit = [self currentConduit];
	[conduit openExternalSite:[[conduits selectedObjects] lastObject]];
}

- (NSArray *) tasksWithField:(NSString *) field matching:(NSObject *) value
{
	NSArray * allTasks = [tasks arrangedObjects];

	return [allTasks filteredArrayUsingPredicate:[NSComparisonPredicate predicateWithLeftExpression:[NSExpression expressionForKeyPath:field]
																					rightExpression:[NSExpression expressionForConstantValue:value]
																						   modifier:NSDirectPredicateModifier
																							   type:NSEqualToPredicateOperatorType options:0]];
}

- (NSDictionary *) mappingForDefinition:(NSDictionary *) definition
{
	NSMutableDictionary * mapping = [NSMutableDictionary dictionary];
	
	if ([[definition valueForKey:@"type"] isEqual:@"Basecamp"])
	{
		[mapping setValue:@"Date Completed" forKeyPath:@"completed_on"];					// *
		[mapping setValue:@"Complete" forKeyPath:@"completed"];								// *
		[mapping setValue:@"Date Created" forKeyPath:@"created_on"];						// *
		[mapping setValue:@"Area" forKeyPath:@"List"];										// *
		[mapping setValue:@"Priority" forKeyPath:@"position"];								// *

		[mapping setValue:@"Content" forKeyPath:@"content"];
		[mapping setValue:@"Completed By" forKeyPath:@"completer_id"];
		[mapping setValue:@"Created By" forKeyPath:@"creator_id"];
		[mapping setValue:@"Date Due" forKeyPath:@"deadline"];
	}
	else if ([[definition valueForKey:@"type"] isEqual:@"iCal"])
	{
		[mapping setValue:@"Project" forKeyPath:@"Calendar"];								// *
	}
	else if ([[definition valueForKey:@"type"] isEqual:@"Things"])
	{
		[mapping setValue:@"Date Completed" forKeyPath:@"datecompleted"];					// *
		[mapping setValue:@"Date Due" forKeyPath:@"datedue"];								// *
		[mapping setValue:@"Last Updated" forKeyPath:@"datemodified"];						// *
	}
	else if ([[definition valueForKey:@"type"] isEqual:@"Entourage"])
	{
		[mapping setValue:@"Project" forKeyPath:@"project list"];							// *
		[mapping setValue:@"Area" forKeyPath:@"category"];									// *
		[mapping setValue:@"Priority" forKeyPath:@"priority"];								// *
		[mapping setValue:@"Last Updated" forKeyPath:@"modification"];						// *
		[mapping setValue:@"Date Due" forKeyPath:@"due date"];								// *
		[mapping setValue:@"Date Completed" forKeyPath:@"completed date"];					// *
		[mapping setValue:@"Complete" forKeyPath:@"completed"];							// *

		[mapping setValue:@"Date Created" forKeyPath:@"start date"];						// *
	}
	else if ([[definition valueForKey:@"type"] isEqual:@"FogBugz"])
	{
		[mapping setValue:@"Last Updated" forKeyPath:@"dtLastUpdated"];
		[mapping setValue:@"Last Opened" forKeyPath:@"dtLastOpened"];
		[mapping setValue:@"Date Completed" forKeyPath:@"dtClosed"];					// *
		[mapping setValue:@"Date Due" forKeyPath:@"dtDue"];
		[mapping setValue:@"Fix For Date" forKeyPath:@"dtFixFor"];
		[mapping setValue:@"Last Viewed" forKeyPath:@"dtLastView"];
		[mapping setValue:@"Date Created" forKeyPath:@"dtOpened"];						// *
		[mapping setValue:@"Date Resolved" forKeyPath:@"dtResolved"];
		
		[mapping setValue:@"Forwarded" forKeyPath:@"fForwarded"];
		[mapping setValue:@"!Complete" forKeyPath:@"fOpen"];
		[mapping setValue:@"Replied" forKeyPath:@"fReplied"];
		[mapping setValue:@"Current Estimate" forKeyPath:@"hrsCurrEst"];
		[mapping setValue:@"Hours Elapsed" forKeyPath:@"hrsElapsed"];
		[mapping setValue:@"Original Estimate" forKeyPath:@"hrsOrigEst"];
		[mapping setValue:@"Bug No." forKeyPath:@"ixBug"];
		[mapping setValue:@"Area" forKeyPath:@"sArea"];
		[mapping setValue:@"Category" forKeyPath:@"sCategory"];
		[mapping setValue:@"Assigned To (E-Mail)" forKeyPath:@"sEmailAssignedTo"];
		[mapping setValue:@"Fix For" forKeyPath:@"sFixFor"];
		[mapping setValue:@"Assigned To" forKeyPath:@"sPersonAssignedTo"];
		[mapping setValue:@"Priority" forKeyPath:@"sPriority"];
		[mapping setValue:@"Project" forKeyPath:@"sProject"];							// *
		[mapping setValue:@"Status" forKeyPath:@"sStatus"];
		[mapping setValue:@"Title" forKeyPath:@"sTitle"];
		
		[mapping setValue:@"Customer E-Mail" forKeyPath:@"sCustomerEmail"];
		[mapping setValue:@"Latest Summary" forKeyPath:@"sLatestTextSummary"];
		[mapping setValue:@"Ticket ID" forKeyPath:@"sTicket"];
	}
	else if ([[definition valueForKey:@"type"] isEqual:@"OmniFocus"])
	{
		[mapping setValue:@"Complete" forKeyPath:@"complete"];					// *
		[mapping setValue:@"Date Completed" forKeyPath:@"completion date"];					// *
		[mapping setValue:@"Date Created" forKeyPath:@"creation date"];						// *
		[mapping setValue:@"Date Due" forKeyPath:@"due date"];
		[mapping setValue:@"Last Updated" forKeyPath:@"modification date"];
		[mapping setValue:@"Project" forKeyPath:@"project"];

		/*
		[mapping setValue:@"Last Updated" forKeyPath:@"dtLastUpdated"];
		[mapping setValue:@"Last Opened" forKeyPath:@"dtLastOpened"];
		[mapping setValue:@"Date Due" forKeyPath:@"dtDue"];
		[mapping setValue:@"Fix For Date" forKeyPath:@"dtFixFor"];
		[mapping setValue:@"Last Viewed" forKeyPath:@"dtLastView"];
		[mapping setValue:@"Date Created" forKeyPath:@"dtOpened"];						// *
		[mapping setValue:@"Date Resolved" forKeyPath:@"dtResolved"];
		
		[mapping setValue:@"Forwarded" forKeyPath:@"fForwarded"];
		[mapping setValue:@"Open" forKeyPath:@"fOpen"];
		[mapping setValue:@"Replied" forKeyPath:@"fReplied"];
		[mapping setValue:@"Current Estimate" forKeyPath:@"hrsCurrEst"];
		[mapping setValue:@"Hours Elapsed" forKeyPath:@"hrsElapsed"];
		[mapping setValue:@"Original Estimate" forKeyPath:@"hrsOrigEst"];
		[mapping setValue:@"Bug No." forKeyPath:@"ixBug"];
		[mapping setValue:@"Area" forKeyPath:@"sArea"];
		[mapping setValue:@"Category" forKeyPath:@"sCategory"];
		[mapping setValue:@"Assigned To (E-Mail)" forKeyPath:@"sEmailAssignedTo"];
		[mapping setValue:@"Fix For" forKeyPath:@"sFixFor"];
		[mapping setValue:@"Assigned To" forKeyPath:@"sPersonAssignedTo"];
		[mapping setValue:@"Priority" forKeyPath:@"sPriority"];
		[mapping setValue:@"Project" forKeyPath:@"sProject"];							// *
		[mapping setValue:@"Status" forKeyPath:@"sStatus"];
		[mapping setValue:@"Title" forKeyPath:@"sTitle"];
		
		[mapping setValue:@"Customer E-Mail" forKeyPath:@"sCustomerEmail"];
		[mapping setValue:@"Latest Summary" forKeyPath:@"sLatestTextSummary"];
		[mapping setValue:@"Ticket ID" forKeyPath:@"sTicket"]; */
	}
	
	return mapping;
}

- (void) refreshConduit:(Conduit *) conduit definition:(NSDictionary *) definition
{
	NSStatusBar * bar = [NSStatusBar systemStatusBar];
	
	NSStatusItem * menuItem = [[bar statusItemWithLength:NSVariableStatusItemLength] retain];
	
	[menuItem setTitle:nil];
	[menuItem setImage:[NSImage imageNamed:@"download"]];
	[menuItem setHighlightMode:NO];
	
	NSDockTile *tile = [[NSApplication sharedApplication] dockTile];
	[tile setBadgeLabel:@"↻"];
	
	NSObject * selectedFilter = [[filters selectedObjects] lastObject];
	[filters setSelectedObjects:nil];

	changeCount = 0;
	changedTasks = [NSMutableArray array];
	
	NSDictionary * mapping = [definition valueForKey:@"mappings"];
	
	if (mapping == nil)
	{
		mapping = [self mappingForDefinition:definition];
		[definition setValue:mapping forKeyPath:@"mappings"];
	}
	
	NSMutableDictionary * def = [NSMutableDictionary dictionaryWithDictionary:definition];
	[def setValue:[self passwordForDefinition:def] forKey:@"password"];
	
	NSArray * fetchedTasks = [conduit fetchTasks:def];
	
	[progressBar stopAnimation:self];
	[progressBar setIndeterminate:NO];
	
	[progressBar setMinValue:0];
	[progressBar setDoubleValue:0];
	[progressBar setMaxValue:(double) [fetchedTasks count]];
	
	NSString * identifier = [conduit identifier];
	
	for (NSDictionary * newTask in fetchedTasks)
	{
		[progressBar incrementBy:1];
		[progressBar display];
			
		[newTask setValue:[definition valueForKey:@"name"] forKey:@"source"];

		NSArray * matches = [self tasksWithField:identifier matching:[newTask valueForKey:identifier]];
		
		if ([matches count] > 0)
		{
			for (NSMutableDictionary * task in matches)
			{
				BOOL changed = NO;
				
				for (NSString * key in [newTask allKeys])
				{
					NSObject * defValue = [newTask valueForKey:key];
					NSString * mapKey = [mapping valueForKey:key];
					
					if (mapKey != nil)
						key = mapKey;
					
					if ([key hasPrefix:@"!"])
					{
						key = [key substringFromIndex:1];
						
						if ([defValue isKindOfClass:[NSNumber class]])
						{
							if ([((NSNumber *) defValue) boolValue])
								defValue = @"false";
							else
								defValue = @"true";
						}
						else if ([defValue isKindOfClass:[NSString class]])
						{
							NSString * v = (NSString *) defValue;
							
							if ([[v lowercaseString] isEqual:@"true"] || [[v lowercaseString] isEqual:@"yes"])
								defValue = @"false";
							else
								defValue = @"true";
						}
					}
					
					if (![[task valueForKey:key] isEqual:defValue])
					{
						[task setValue:defValue forKey:key];
						changed = YES;
					}
				}

				if (changed)
				{
					[changedTasks addObject:[task valueForKey:@"name"]];
					changeCount = changeCount + 1;
					[task setValue:[NSDate date] forKeyPath:@"Local Modification Date"];
				}
			}
		}
		else
		{
			NSMutableDictionary * task = [[NSMutableDictionary alloc] init];
			
			[task setValue:[newTask valueForKey:@"name"] forKey:@"name"];
			
			for (NSString * key in [newTask allKeys])
			{
				NSObject * defValue = [newTask valueForKey:key];
				NSString * mapKey = [mapping valueForKey:key];
				
				if (mapKey != nil)
					key = mapKey;
				
				if ([key hasPrefix:@"!"])
				{
					key = [key substringFromIndex:1];
					
					if ([defValue isKindOfClass:[NSNumber class]])
					{
						if ([((NSNumber *) defValue) boolValue])
							defValue = @"false";
						else
							defValue = @"true";
					}
					else if ([defValue isKindOfClass:[NSString class]])
					{
						NSString * v = (NSString *) defValue;
						
						if ([[v lowercaseString] isEqual:@"true"] || [[v lowercaseString] isEqual:@"yes"])
							defValue = @"false";
						else
							defValue = @"true";
					}
				}					
				
				[task setValue:defValue forKey:key];
			}

			[task setValue:[NSDate date] forKeyPath:@"Local Modification Date"];
			[tasks addObject:task];
			
			changeCount = changeCount + 1;
			
			[changedTasks addObject:[task valueForKey:@"name"]];
			
			[task release];
		}
	}	
	
	if (changeCount > 0)
	{
		if (changeCount < 13)
		{
			for (NSString * name in changedTasks)
			{
				[GrowlApplicationBridge notifyWithTitle:[NSString stringWithFormat:@"%@ Tasks Updated", [definition valueForKey:@"name"]]
											description:[NSString stringWithFormat:@"Updated '%@'.", name]
									   notificationName:@"Conduit Updated"
											   iconData:nil
											   priority:0
											   isSticky:NO
										   clickContext:nil];
			}
		}
		
		[GrowlApplicationBridge notifyWithTitle:[NSString stringWithFormat:@"%@ Tasks Updated", [definition valueForKey:@"name"]]
									description:[NSString stringWithFormat:@"%d tasks updated in the local database.", changeCount]
							   notificationName:@"Conduit Updated"
									   iconData:nil
									   priority:0
									   isSticky:NO
								   clickContext:nil];
	}

	[progressBar setIndeterminate:YES];
	[progressBar startAnimation:self];

	if (selectedFilter != nil)
		[filters setSelectedObjects:[NSArray arrayWithObject:selectedFilter]];

	[[NSNotificationCenter defaultCenter] postNotificationName:@"refresh_fields" object:nil];

	[tile setBadgeLabel:nil];

	[bar removeStatusItem:menuItem];
	[menuItem release];
}

- (void) updateMappings:(NSMutableDictionary *) def withConduit:(Conduit *) conduit
{
	NSMutableDictionary * mappings = [def valueForKey:@"mappings"];
	
	if (mappings == nil)
		mappings = [NSMutableDictionary dictionary];

	for (NSDictionary * field in [fields arrangedObjects])
	{
		NSString * name = [field valueForKey:@"name"];
		
		NSString * value = [mappings valueForKey:name];
		
		if (value == nil)
			[mappings setValue:name forKey:name];
	}
	
	[def setValue:mappings forKey:@"mappings"];
}

- (IBAction) refresh:(id) sender
{
	Conduit * conduit = [self currentConduit];
	NSMutableDictionary * def = [[conduits selectedObjects] lastObject];

	NSWindow * keyWindow = [NSApp keyWindow];
	
	if (keyWindow != nil)
	{
		[progressBar setUsesThreadedAnimation:YES];
		[progressBar startAnimation:sender];
		[progressMessage setStringValue:[NSString stringWithFormat:@"Refreshing %@...", [def valueForKey:@"name"], nil]];

		[NSApp beginSheet:progressWindow modalForWindow:keyWindow modalDelegate:self didEndSelector:nil contextInfo:NULL];
	}

	[self refreshConduit:conduit definition:def];
	[self updateMappings:def withConduit:conduit];

	if (keyWindow != nil)
	{
		[NSApp endSheet:progressWindow];
		[progressWindow orderOut:self];
		[progressBar stopAnimation:sender];
	}
}

- (IBAction) refreshAll:(id) sender
{
	NSEnumerator * defIter = [[conduits arrangedObjects] objectEnumerator];
	NSMutableDictionary * def = nil;
	while (def = [defIter nextObject])
	{
		Conduit * conduit = [self conduitForDefinition:def];
		
		NSWindow * keyWindow = [NSApp keyWindow];
		
		if (keyWindow != nil)
		{
			[progressBar setUsesThreadedAnimation:YES];
			[progressBar startAnimation:sender];
			[progressMessage setStringValue:[NSString stringWithFormat:@"Refreshing %@...", [def valueForKey:@"name"], nil]];
			
			[NSApp beginSheet:progressWindow modalForWindow:keyWindow modalDelegate:self didEndSelector:nil contextInfo:NULL];
		}
		
		[self refreshConduit:conduit definition:def];
		[self updateMappings:def withConduit:conduit];
		
		if (keyWindow != nil)
		{
			[NSApp endSheet:progressWindow];
			[progressWindow orderOut:self];
			[progressBar stopAnimation:sender];
		}
	}
}

- (IBAction) finishMapping:(id) sender
{
	[NSApp endSheet:mappingWindow];
	[mappingWindow orderOut:self];
}

- (IBAction) showMapping:(id) sender
{
	NSMutableDictionary * def = [[conduits selectedObjects] lastObject];

	NSDictionary * mapping = [def valueForKey:@"mappings"];
	
	if (mapping == nil)
	{
		mapping = [self mappingForDefinition:def];
		[def setValue:mapping forKeyPath:@"mappings"];
	}
	
	[NSApp beginSheet:mappingWindow modalForWindow:conduitsWindow modalDelegate:self didEndSelector:nil contextInfo:NULL];
}

- (NSDictionary *) registrationDictionaryForGrowl
{
	NSArray * notifications = [NSArray arrayWithObject:@"Conduit Updated"];
	
	NSMutableDictionary * dict = [NSMutableDictionary dictionary];
	[dict setValue:notifications forKeyPath:GROWL_NOTIFICATIONS_ALL];
	[dict setValue:notifications forKeyPath:GROWL_NOTIFICATIONS_DEFAULT];
	
	return dict;
}

@end
